#
# Copyright 2009, Jeffrey Palm.
#

#
# Finds the currently playing track in itunes then sends it along with
# our identity.  Examples:
#
#  % resumep3
#  % resumep3 jeff
#
# For me, these are equal
#

require 'cgi'
require 'util'
require 'itunes'
require 'server'
require 'config'
require 'open-uri'
require 'rexml/document'
require 'common'

# The main class that together all the different parts:
#
#  - Takes care of storing/retrieving the shared key that is used to
#    communicate with other people.
#  - It manages starting/stopping itunes.
#  - Starts up the server that gets the request from the phone on the
#    local network.
#
class Resumep3 < Common

  # Applescript to print out the currently-playing itunes track
  ITUNES_SCRIPT = "tell application \"iTunes\"\n" +
    "	set t to current track\n" +
    "	(player state as string) & \"	\" & (artist of t) & \"	\" & (name of t) & \"	\" & (album of t) & \"	\" & (player position) & \"	\" & (track number of t) & \"	\" & (duration of t)\n" +
    "end tell\n"

  # Applescript to play the track with passed in track id
  PLAY_SCRIPT = "on run argv\n" +
    "	tell application \"iTunes\"\n" +
    "		set the_id to item 1 of argv as number\n" +
    "		activate\n" +
    "		set t to some track whose database ID is the_id\n" +
    "		play t\n" +
    "	end tell\n" +
    "end run\n"

  # Applescript to pause/play itunes
  PAUSE_SCRIPT = "on run argv\n" +
    "	tell application \"iTunes\"\n" +
    "		pause\n" +
    "	end tell\n" +
    "end run\n"

  # Members
  attr_reader :itunes;
  
  def initialize(base_url=nil)
    super  base_url
    @itunes = ITunes.new_instance
  end


  # ----------------------------------------------------------------------
  # Implementation of Common
  # ----------------------------------------------------------------------

  # Returns info about the currently-playing song in itunes
  def read_info(user)
    parts = run_script ITUNES_SCRIPT
    return {
      'state'    => get_state(parts.shift),
      'artist'   => parts.shift,
      'track'    => parts.shift,
      'album'    => parts.shift,
      'time'     => parts.shift,
      'user'     => user
    }
  end

  # Start the loop to wait for the shared key; ends once we have it
  def find_the_shared_key(has_a_shared_key)
    note 'Begin waiting for the shared key'
    note 'IP Address = ' + private_ip_address  
    s = Server.new SharedKeyHandler.new(has_a_shared_key)
    s.start '127.0.0.1',DEFAULT_PORT
    note 'Done waiting for the shared key'
    @shared_key = has_a_shared_key.shared_key
    note 'Shared key: ' + @shared_key
  end

  # Plays the track with id 'track_id' in itunes
  def play_track(track_id)
    run_script PLAY_SCRIPT,[track_id]
  end

  # Pauses itunes
  def pause_player
    run_script PAUSE_SCRIPT
  end

  # Given the song 'info', try to find the matching track in our
  # itunes library and return that.
  def find_track(info)
    find_track_from_xml @itunes.library
  end


  # ----------------------------------------------------------------------
  # Utility methods
  # ----------------------------------------------------------------------

  # Returns PLAYING or PAUSED for itunes PLAYING is returned unless we
  # are playing in tunes -- i.e. paused, stopped are the same
  def get_state(s)
    return PLAYING if s =~ /kPSP/ || s =~ /play/
    return PAUSED  if s =~ /kPSp/ || s =~ /paus/ #retarded!!!
    return s
  end

  # ----------------------------------------------------------------------
  # Managing the stored shared key
  # ----------------------------------------------------------------------

  # Instances of this class are responsible for recieving requests on
  # a socket that contain the shared key, storing it, and then giving
  # it to the enclosing instance
  class SharedKeyHandler < Server::Handler

    # Pass in the enclosing instance of Resumep3
    def initialize(has_a_shared_key)
      super 'Tortilla Bob'
      @has_a_shared_key = has_a_shared_key
    end

    # Here, we:
    #
    #   1. Take the shared key from the request.
    #   2. Write it out to disk for later.
    #   3. Give it to the enclosing instance.
    #
    # If anything goes wrong we return 'false'.  But if we're able to
    # get a shared key, store it, and give it up, we return 'true'.
    #
    def handle(request)

      # We'll assume now that the entire request is the key
      begin
        note 'Setting the shared key to ' + request
        @has_a_shared_key.shared_key = request
      rescue Exception => e

        # For an exception we need to keep listening probably to get
        # another shared key
        return error e
      end

      # If we've gotten this far, we don't want to listen for the key
      # any more, so return 'true', meaning we're done
      true
    end
  end

  # ----------------------------------------------------------------------
  # Command line
  # ----------------------------------------------------------------------
 
  # Returns a value starting with RET_, does the dirty work
  def real_main(argv)

    # Options to 'run'
    pause             = false
    resume            = false
    just_check        = false
    motions           = false
    remove_shared_key = false

    # Arguments
    real_args = []
    argv.each do |arg|
      if arg == '-r' || arg =~ /-resume/
        resume = true 
      elsif arg == '-p' || arg =~ /-pause/
        pause = true 
      elsif arg == '-c' || arg =~ /-check/
        just_check = true
      elsif arg == '-n' || arg =~ /-motions/
        motions = true
      elsif arg == '-b' || arg =~ /-verbose/
        @verbose = true
      elsif arg == '-h' || arg =~ /-help/
        print_usage
        return RET_PRINTED_HELP
      elsif arg == '-d' || arg =~ /-deletesharedkey/
        remove_shared_key = true
      elsif arg =~ /^-/
        note 'Invalid argument ' + arg
      else
        real_args << arg
      end
    end
    
    # The shared key may be passed on the command line, it'd be the
    # only argument left standing
    @shared_key = real_args.shift if !real_args.empty?
    note 'Shared key: ' + @shared_key.to_s

    # This is where all the implementations are the same
    run has_options
  end


  # ----------------------------------------------------------------------
  # Main entry point
  # ----------------------------------------------------------------------

  def main(argv)
    exit real_main argv
  end


  # ----------------------------------------------------------------------
  # Private
  # ----------------------------------------------------------------------

  private

  # Prints the options for this program
  def print_usage
    STDERR.puts 'Usage: ' + File.basename($0) + ' [options] <user>?'
    STDERR.puts 'where options include:'
    STDERR.puts ' -r || -resume           force to resume'
    STDERR.puts ' -p || -pause            force to pause'
    STDERR.puts ' -c || -check            just print the currently-saved song'
    STDERR.puts ' -n || -motions          just print what we would do'
    STDERR.puts ' -h || -help             print this message'
    STDERR.puts ' -v || -verbose          be really loud'
    STDERR.puts ' -d || -deletesharedkey  delete shared key and exit'
  end

end
